package s3

import (
	"net/http"
	"net/url"
	"strings"

	"github.com/mars/internal/pkg/code"
	"github.com/mars/third_part/fork/aws/signature"
	"github.com/mars/third_part/fork/errors"
)

type S3Request struct {
	Ak             string
	Method         string
	Bucket         string
	Object         string
	RequestURI     string
	MasterUploadId string
	SlaveUploadId  string
	SignType       signature.SignatureType
	ClusterCode    string
}

// S3OptParse 负责解析S3的请求
type S3OptParser interface {
	// Parse 根据 request 解析 s3 的信息，shouldParseUploadId 将解析拆分 uploadId 如果设置为 true
	Parse(r *http.Request) (*S3Request, S3Opt, error)
}

type S3OptParserFunc func(*http.Request) (*S3Request, S3Opt, error)

func (fn S3OptParserFunc) Parse(r *http.Request) (*S3Request, S3Opt, error) {
	return fn(r)
}

func PathParser(r *http.Request) (s3Request *S3Request, opts S3Opt, err error) {
	var (
		path = r.URL.Path

		ak       string
		bucket   string
		object   string
		signType signature.SignatureType
	)

	// 从请求中解析出ak
	ak, signType, err = signature.GetAccessKeyWithSignType(r)
	if err != nil {
		err = errors.WithCodeE(err, code.ErrParseAkFromHeader)
		return
	}

	opts |= S3Opt(methodParse(r.Method) << readWriteriteShift)
	// path 处理
	if path == "" {
		path = r.URL.RawPath
	}
	if path != "" && path != "/" {
		if path[len(path)-1] == '/' {
			path = path[:len(path)-1]
		}
		if path[0] == '/' {
			path = path[1:]
		}

		var paths = strings.Split(path, "/")
		if len(paths) < 1 {
			err = errors.WithCode(code.ErrUnsupportRequest, "paths is invalid: %s", path)
			return
		}

		// bucket操作
		if len(paths) == 1 {
			opts |= baseBit << 62
			bucket = paths[0]
		} else if len(paths) == 2 { // object操作
			opts |= baseBit << 61
			bucket = paths[0]
			object = paths[1]
		} else {
			opts |= baseBit << 61
			bucket = paths[0]
			object = strings.Join(paths[1:], "/")
		}
	} else {
		// bucket 操作,列出 buckets
		opts |= baseBit << 62
	}

	s3Request = &S3Request{
		Ak:         ak,
		Method:     r.Method,
		Bucket:     bucket,
		Object:     object,
		RequestURI: r.RequestURI,
		SignType:   signType,
	}

	// 根据请求 uri 判断是否是分片上传
	query := r.URL.Query()
	switch {
	case queryHas(query, "uploads") && opts.IsPost():
		opts |= PartInit
	case queryHas(query, "partNumber") && queryHas(query, "uploadId") && opts.IsPut():
		opts |= PartPut
		parseUploadId(query, s3Request)
	case queryHas(query, "uploadId") && opts.IsPost():
		opts |= PartComplete
		parseUploadId(query, s3Request)
	case queryHas(query, "uploadId") && opts.IsDelete():
		opts |= PartAbort
		parseUploadId(query, s3Request)
	}
	return
}

func parseUploadId(query url.Values, sink *S3Request) {
	uploadId := query.Get("uploadId")
	uploadIds := strings.Split(uploadId, UploadIdSplit)

	// 如果只有一个 uploadId 说明分片上传init操作时期内，只有一个节点存活，
	// 此时只需要把uploadId赋值给 master 即可
	// 如果发生主从切换，则分片整个事务都要上传失败
	if len(uploadIds) == 1 {
		sink.MasterUploadId = uploadId
		// sink.SlaveUploadId = uploadId
		return
	}
	sink.MasterUploadId = uploadIds[0]
	sink.SlaveUploadId = uploadIds[1]
}

func methodParse(method string) int64 {
	switch strings.ToUpper(method) {
	case http.MethodGet:
		return 0b10000
	case http.MethodHead:
		return 0b01000
	case http.MethodPut:
		return 0b00100
	case http.MethodPost:
		return 0b00010
	case http.MethodDelete:
		return 0b00001
	default:
		return 0
	}
}

// DomainAndPathParser 泛域名解析操作：目前不需要实现（2022-08）TODO
// func DomainAndPathParser(r *http.Request) (s3Request *S3Request, opts S3Opt, err error) {}

func queryHas(v url.Values, key string) bool {
	_, ok := v[key]
	return ok
}
